from pandac.PandaModules import *    # basic Panda modules
from direct.showbase.ShowBase import ShowBase
from direct.filter.CommonFilters import CommonFilters
from direct.interval.IntervalGlobal import *  # for compound intervals
from direct.task import Task         # for update fuctions
from panda3d.core import *
import cfg
import sys
import math
import random
import enemies
from turret import Turret
from FollowCam import FollowCam
from terrain import Terrain
from player import Player
from projectile import Projectile
from gui import GUI

class World(ShowBase): # subclassing here is necessary to accept event
    def __init__(self):
        # turn off default mouse control, otherwise can't reposition camera
        ShowBase.__init__(self)
        base.disableMouse()
        self.enableParticles()
        self.terrain = Terrain() 
        self.loadModels()
        self.setupCameras()
        self.setupLights()
        self.setupPhysics()
        self.setupCollisions()
        render.setShaderAuto() # turns on per-pixel lighting, normal mapping, etc (you probably want to use this)
        #self.keyMap = {"left":0, "right":0, "forward":0}
        #taskMgr.add(self.move, "moveTask")
        taskMgr.add(self.updateSpotlight, "spotlightTask")
        self.prevtime = 0
        self.isMoving = False
        taskMgr.add(self.turnEnemy, "turnEnemyTask")
        taskMgr.add(self.updateEnemies , "updateEnemiesTask")

        self.gui = GUI()
        
        self.followCam = FollowCam(self, self.cameras[0], self.player.model)
        self.setupBloomEffects(self.cameras[0])

        base.disableMouse()
        props = WindowProperties.getDefault()
        w=base.pipe.getDisplayWidth()
        h=base.pipe.getDisplayHeight()
        props.setSize(w,h)
        props.setFullscreen(True)
        props.setCursorHidden(True)
        base.win.requestProperties(props)
        #self.player.resetMouse()
        
        #top down view
        #self.topCamera = self.makeCamera(self.win)
        #camera.reparentTo(self.terrain.model)
        #camera.setPosHpr(0, 0, 45, 0, -90, 0)

        #Bloom effects
        #self.filters = CommonFilters(base.win, base.cam)
        #filterok = self.filters.setBloom(blend=(0,0,0,1), desat=-0.5, intensity=3.0, size="small")
        #if (filterok == False):
        #    addTitle("Toon Shader: Video card not powerful enough to do image postprocessing")
        #    return
        #self.glowSize=1
        
        self.accept("escape", sys.exit)
        self.accept("arrow_up", self.player.setKey, ["forward", 1])
        self.accept("arrow_left", self.player.setKey, ["left", 1])
        self.accept("arrow_right", self.player.setKey, ["right", 1])
        self.accept("arrow_down", self.player.setKey, ["back", 1])
        self.accept("w", self.player.setKey, ["forward", 1])
        self.accept("a", self.player.setKey, ["left", 1])
        self.accept("d", self.player.setKey, ["right", 1])
        self.accept("s", self.player.setKey, ["back", 1])
        self.accept("mouse1", self.player.setKey, ["shoot", 1])
        self.accept("r", self.useCamera, [1])
        self.accept("1", self.player.setMode, [0])
        self.accept("2", self.player.setMode, [1])
        self.accept("3", self.player.setMode, [2])
        self.accept("wheel_up", self.player.cycleMode, [-1])
        self.accept("wheel_down", self.player.cycleMode, [1])
        self.accept("arrow_up-up", self.player.setKey, ["forward", 0])
        self.accept("arrow_left-up", self.player.setKey, ["left", 0])
        self.accept("arrow_right-up", self.player.setKey, ["right", 0])
        self.accept("arrow_down-up", self.player.setKey, ["back", 0])
        self.accept("w-up", self.player.setKey, ["forward", 0])
        self.accept("a-up", self.player.setKey, ["left", 0])
        self.accept("d-up", self.player.setKey, ["right", 0])
        self.accept("s-up", self.player.setKey, ["back", 0])
        self.accept("mouse1-up", self.player.setKey, ["shoot", 0])
        self.accept("r-up", self.useCamera, [0])
        #self.accept("playerRay-hit-collisionPlane", self.player.changeZ)
        self.accept("tankenemy-plasma-hit-player", self.player.plasmaHit)
        self.accept("tankenemy-acid-hit-player", self.player.plasmaHit)
        self.accept("tankenemy-laser-hit-player", self.player.plasmaHit)
        self.accept("acidenemy-plasma-hit-player", self.player.acidHit)
        self.accept("acidenemy-acid-hit-player", self.player.acidHit)
        self.accept("acidenemy-laser-hit-player", self.player.acidHit)
        self.accept("laserenemy-plasma-hit-player", self.player.laserHit)
        self.accept("laserenemy-acid-hit-player", self.player.laserHit)
        self.accept("laserenemy-laser-hit-player", self.player.laserHit)
        self.accept("space", self.player.jump)
        self.accept("player-plasma-hit-tank", self.enemy.gotHit)
        self.accept("player-acid-hit-tank", self.enemy.gotHit)
        self.accept("player-laser-hit-tank", self.enemy.gotHit)
        
        # "mouse1" is the event when the left mouse button is clicked
        # append "-up" for the equivalent of a pygame.KEYUP event

    def setupCameras(self):
        self.cameras = [self.makeCamera(self.win), self.makeCamera(self.win)]
        
        self.cam.node().getDisplayRegion(0).setActive(0) #turn off main camera
        #Rear-view camera
        self.cameras[1].reparentTo(self.player.model)
        self.cameras[1].setPosHpr(0,-50,25,0,-20,0)
        self.cameras[1].node().getDisplayRegion(0).setActive(0)
    
    
    def useCamera(self, cam):
        if cam == 0:
            #Turn on front facing camera
            self.cameras[0].node().getDisplayRegion(0).setActive(1)
            self.cameras[1].node().getDisplayRegion(0).setActive(0)
        else:
            #Turn on rear view camera
            self.cameras[0].node().getDisplayRegion(0).setActive(0)
            self.cameras[1].node().getDisplayRegion(0).setActive(1)
    
    def setupBloomEffects(self, c):
        self.filters = []
        #for c in self.cameras:

        f = CommonFilters(base.win, c)
        fOk = f.setBloom(blend=(0,0,0,1), desat=-0.5, intensity=3.0, size="small")
        if (fOk == False):
            addTitle("Toon Shader: Video card not powerful enough to do image postprocessing")
            return
        self.filters.append(f)
    
    def setupPhysics(self):
        base.enableParticles()
        gravNode = ForceNode("gravity")
        render.attachNewNode(gravNode)
        gravityForce = LinearVectorForce(0, 0, -9.81)
        gravNode.addForce(gravityForce)
        base.physicsMgr.addLinearForce(gravityForce)
        
    def loadModels(self):
        """loads initial models into the world"""
        self.player = Player()
        cfg.player = self.player
        self.player.model.setH(180)
        self.player.model.setPos(0,0,2)
        cfg.enemy = self.enemy = enemies.TankEnemy()
        # self.tankEnemy = enemies.TankEnemy()
        # self.laserEnemy = enemies.LaserEnemy()
        # self.acidEnemy = enemies.AcidEnemy()
        
    def setupLights(self):
        # ambient light
        self.ambientLight = AmbientLight("ambientLight")
        # four values, RGBA (alpha is largely irrelevant), value range is 0:1
        self.ambientLight.setColor((0.2, 0.2, 0.2, 1))
        self.ambientLightNP = render.attachNewNode(self.ambientLight)
        # the nodepath that calls setLight is what gets illuminated by the light
        render.setLight(self.ambientLightNP)
        # call clearLight() to turn it off
        
        self.keyLight = DirectionalLight("keyLight")
        self.keyLight.setColor((0.5, 0.5, 0.5, 1))
        self.keyLightNP = render.attachNewNode(self.keyLight)
        self.keyLightNP.setHpr(0, -26, 0)
        render.setLight(self.keyLightNP)
        
        self.fillLight = DirectionalLight("fillLight")
        self.fillLight.setColor((0.3, 0.3, 0.3, 1))
        self.fillLightNP = render.attachNewNode(self.fillLight)
        self.fillLightNP.setHpr(45, 0, 0)
        render.setLight(self.fillLightNP)
        
        self.enemyLight1 = Spotlight("enemyLight1")
        self.enemyLight1.setLens(PerspectiveLens())
        self.enemyLight1.setShadowCaster(True, 512, 512)
        self.enemyLightNP1 = render.attachNewNode(self.enemyLight1)
        self.enemyLightNP1.setPos(0,0, 100)
        self.enemyLightNP1.lookAt(self.enemy.model)
        render.setLight(self.enemyLightNP1)
        
        # self.enemyLight2 = Spotlight("enemyLight2")
        # self.enemyLightNP2 = self.terrain.model.attachNewNode(self.enemyLight2)
        # self.enemyLightNP2.setPos(-12,12, 100)
        # render.setLight(self.enemyLightNP2)
        
        # self.enemyLight3 = Spotlight("enemyLight3")
        # self.enemyLightNP3 = self.terrain.model.attachNewNode(self.enemyLight3)
        # self.enemyLightNP3.setPos(12,-12, 100)
        # render.setLight(self.enemyLightNP3)
        
        # self.enemyLight4 = Spotlight("enemyLight4")
        # self.enemyLightNP4 = self.terrain.model.attachNewNode(self.enemyLight4)
        # self.enemyLightNP4.setPos(-12,-12, 100)
        # render.setLight(self.enemyLightNP4)
        
    def updateSpotlight(self, task):
        if self.enemy.model:
            self.enemyLightNP1.lookAt(self.enemy.model)
        return task.cont
    
    def walk(self):
        """compound interval for walking"""
        dist = 5
        angle = deg2Rad(self.panda.getH())
        dx = dist * math.sin(angle)
        dy = dist * -math.cos(angle)
        pandaWalk = Parallel(self.panda.posInterval(1, (self.panda.getX() + dx, \
            self.panda.getY() + dy, 0)),
            self.panda.actorInterval("walk", loop = 1, duration = 1))
        pandaWalk.start()
        # some interval methods:
        #   start(), loop(), pause(), resume(), finish()
        #       start() can take arguments: start(starttime, endtime, playrate)
    
    def turn(self, direction):
        pandaTurn = self.panda.hprInterval(0.2, (self.panda.getH() - direction * 10, 0, 0)) 
        pandaTurn.start()
        
    def setupCollisions(self):
        # instantiates a collision traverser and sets it to the default
        base.cTrav = CollisionTraverser()
        #self.cHandler = CollisionHandlerEvent()
        # set pattern for event sent on collision
        #   "%in" is substituted with the name of the intro object
        
        cPlayerSphere = CollisionSphere((0, 0, 5), 5) # 500 because the panda is scaled way down
        cPlayerNode = CollisionNode("player")
        cPlayerNode.addSolid(cPlayerSphere)
        #cPlayerNode.setIntoCollideMask(BitMask32.allOff()) # panda is *only* a from object (optional here)
        #cPlayerNode.setCollideMask(BitMask32(0xA))
        #print cPlayerNode.getCollideMask()
        cPlayerNodePath = self.player.model.attachNewNode(cPlayerNode)
        #cPlayerNodePath.show()
        
        # cTankSphere = CollisionSphere((0, 0, 4), 7)
        # cTankNode = CollisionNode("tank")
        # cTankNode.addSolid(cTankSphere)
        # #cTankNode.setCollideMask(BitMask32(0xB))
        # cTankNodePath = self.tankEnemy.model.attachNewNode(cTankNode)
        # cTankNodePath.show()

        # caTankSphere = CollisionSphere((0, 0, 4), 7)
        # caTankNode = CollisionNode("a_tank")
        # caTankNode.addSolid(caTankSphere)
        # #caTankNode.setCollideMask(BitMask32(0xB))
        # caTankNodePath = self.acidEnemy.model.attachNewNode(caTankNode)
        # caTankNodePath.show()

        # clTankSphere = CollisionSphere((0, 0, 4), 7)
        # clTankNode = CollisionNode("l_tank")
        # clTankNode.addSolid(clTankSphere)
        # #caTankNode.setCollideMask(BitMask32(0xB))
        # clTankNodePath = self.laserEnemy.model.attachNewNode(clTankNode)
        # clTankNodePath.show()
        
        # registers a from object with the traverser with a corresponding handler
        
        self.player.setupCollisions()
        self.enemy.setupCollisions()
        # self.laserEnemy.setupCollisions()
        # self.acidEnemy.setupCollisions()
        
        
        # base.cTrav.addCollider(self.player.gColNp, self.player.gHandler)
        
        #base.cTrav.showCollisions(render)
        
                    
    def turnEnemy(self, task):
        if self.enemy.name == "Destructor":
            if self.enemy.model:
                self.enemy.model.headsUp(self.player.model)
                self.enemy.model.setH(self.enemy.model.getH() + 180)
        return task.cont

    def endEnemyTasks(self):
        taskMgr.remove("enemyMoveTask")
        taskMgr.remove("enemy attempting to shoot")
        for p in cfg.projectilelist:
            taskMgr.remove("plasmaTarget")

    def updateEnemies(self, task):
        if self.enemy.health <= 0:                                #it was killed
            self.enemy.model.removeNode()
            if self.enemy.name == "Destructor":
                self.endEnemyTasks()
                self.enemy = enemies.LaserEnemy()
                self.enemy.setupCollisions()
                self.accept("player-plasma-hit-l_tank", self.enemy.gotHit)
                self.accept("player-acid-hit-l_tank", self.enemy.gotHit)
                self.accept("player-laser-hit-l_tank", self.enemy.gotHit)
            
            elif self.enemy.name == "Annoying":
                self.endEnemyTasks()
                self.enemy = enemies.AcidEnemy()
                self.enemy.setupCollisions()
                self.accept("player-plasma-hit-a_tank", self.enemy.gotHit)
                self.accept("player-acid-hit-a_tank", self.enemy.gotHit)
                self.accept("player-laser-hit-a_tank", self.enemy.gotHit)
            
            elif self.enemy.name == "Squidward":
                for p in cfg.projectilelist:
                    taskMgr.remove("projectileMove")
                self.endEnemyTasks()
                print "You Won!!"
                #display a winning screen
            
        # if self.tankEnemy:
            # if self.tankEnemy.health <= 0:
                # self.tankEnemy.model.removeNode()
                # self.tankEnemy = 0
                # self.laserEnemy = enemies.LaserEnemy()
                # self.laserEnemy.setupCollisions()
        
        # if self.laserEnemy:
            # if self.laserEnemy.health <= 0:
                # self.laserEnemy.model.removeNode()
                # self.laserEnemy = 0
                # self.acidEnemy = enemies.AcidEnemy()
                # self.acidEnemy.setupCollisions()
        
        # if self.acidEnemy:
            # if self.acidEnemy.health <= 0:
                # self.acidEnemy.model.removeNode()
                # self.acidEnemy = 0
                # #display a winning screen
                
        return task.cont
        

# only creates a world when main is being run, not just imported
if __name__ == "__main__":
    w = World()
    run()















